﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Net;
using DownloadMag.Core;

namespace DownloadMag
{
    #region 任务分段信息

    /// <summary>
    /// 任务分段线程（分段下载状态）
    /// </summary>
    public class DTaskConfig : IDisposable
    {
        /// <summary>
        /// 新建分段线程[自动初始化]
        /// </summary>
        /// <param name="taskMag">所属任务管理器</param>
        /// <param name="location">下载地址</param>
        internal DTaskConfig(DTaskMag taskMag, ResourceLocation location, ProxySettings proxy)
        {
            m_mstream = new MemoryStream();
            m_TaskMag = taskMag;
            m_Location = location;
            m_TaskConfigData = new DTaskConfigData();
            m_TaskConfigData.Proxy = proxy;
            m_State = WorkThreadState.Stopped;
            m_AcceptRanges = true;
        }

        /// <summary>
        /// 使用已有分段信息 新建分段线程
        /// </summary>
        /// <param name="taskMag">所属任务管理器</param>
        /// <param name="location">下载地址</param>
        /// <param name="taskConfigData">分段信息</param>
        internal DTaskConfig(DTaskMag taskMag, ResourceLocation location, DTaskConfigData taskConfigData)
        {
            m_mstream = new MemoryStream();
            m_TaskMag = taskMag;
            m_Location = location;
            m_TaskConfigData = taskConfigData;
            m_IsDataInitialized = true;
            m_State = WorkThreadState.Stopped;
            m_AcceptRanges = true;
        }
        #region 属性
        
        private ResourceLocation m_Location;
        private bool m_IsDataInitialized;
        private DTaskConfigData m_TaskConfigData;
        private delegate void work();
        private int m_ErrorCount;
        private Thread m_Thread;
        private DTaskMag m_TaskMag;
        private MemoryStream m_mstream;
        private bool m_IsInitialized;
        private int m_Waittime;
        private bool m_AcceptRanges;    // 是否支持断点续传
        /// <summary>
        /// 事件 线程同步锁
        /// </summary>
        private readonly Object m_eventLock = new Object();
        /// <summary>
        /// 缓存 线程同步锁
        /// </summary>
        private readonly Object m_mstreamLock = new Object();
        /// <summary>
        /// 缓存 线程同步锁
        /// </summary>
        private readonly Object m_mstreamCountLock = new Object();

        /// <summary>
        /// 获取当前线程的错误次数
        /// </summary>
        public int ErrorCount
        {
            get { return m_ErrorCount; }
        }
        /// <summary>
        /// 获取/设置 分段初始化状态
        /// </summary>
        internal bool IsInitialized
        {
            get { return m_IsInitialized; }
            set { m_IsInitialized = value; }
        }
        /// <summary>
        /// 设置/获取 等待时间（毫秒）
        /// </summary>
        internal int Waittime
        {
            get { return m_Waittime; }
            set { m_Waittime = value; }
        }
        /// <summary>
        /// 设置/获取下载任务管理器
        /// </summary>
        internal DTaskMag TaskMag
        {
            get { return m_TaskMag; }
        }
        /// <summary>
        /// 当前缓冲数据大小
        /// </summary>
        internal long MemoryCount
        {
            get
            {
                lock (m_mstreamCountLock)
                {
                    return (m_mstream != null ? m_mstream.Length : 0);
                }
            }
        }
        /// <summary>
        /// 当前已下载数据大小
        /// </summary>
        internal long CurMIndex
        {
            get
            {
                lock (m_mstreamCountLock)
                {
                    return m_TaskConfigData.CurIndex + (m_mstream != null ? m_mstream.Length : 0);
                }
            }
        }
        private bool m_isCompleted; // 下载结束标识
        /// <summary>
        /// 分段是否已完成[写入文件]
        /// </summary>
        public bool IsCompleted
        {
            get
            {
                if (m_TaskConfigData.EndIndex > 0)
                {   // 在已知分段大小的情况下，当前已写入位置等于分段结束位置则表示分段已完全写入
                    return m_TaskConfigData.CurIndex == m_TaskConfigData.EndIndex + 1;
                }
                else
                {   // 在分段大小未知的情况下，仅当下载结束标识为真且数据缓冲区为0，才表示分段完全写入
                    return m_isCompleted && MemoryCount == 0;
                }
            }
        }
        /// <summary>
        /// 分段数据是否已完全下载
        /// </summary>
        public bool IsDownloaded
        {
            get
            {
                if (m_TaskConfigData.EndIndex > 0)
                {   // 在已知分段大小的情况下，当前已下载数据位置等于分段结束位置则表示分段已完全写入
                    return CurMIndex == m_TaskConfigData.EndIndex + 1;
                }
                else
                {   // 在分段大小未知的情况下，直接返回下载结束标识
                    return m_isCompleted;
                }
            }
        }
        /// <summary>
        /// 获取一个状态值表示当前线程是否处于合法运行状态 [子线程内部使用]
        /// </summary>
        private bool IsCurrentRunning
        {
            get { return State == WorkThreadState.Started && Thread.CurrentThread.Equals(m_Thread); }
        }
        /// <summary>
        /// 当前块的工作线程
        /// </summary>
        internal Thread WorkThread
        {
            get { return m_Thread; }
        }
        /// <summary>
        /// 获取当前线程的执行状态
        /// </summary>
        internal bool IsThreadAlive
        {
            get { return m_Thread != null && m_Thread.IsAlive; }
        }

        internal DTaskConfigData TaskConfigData
        {
            get { return m_TaskConfigData; }
        }
        /// <summary>
        /// 分段数据是否已初始化
        /// </summary>
        public bool IsDataInitialized
        {
            get { return m_IsDataInitialized; }
        }
        /// <summary>
        /// 起始偏移量
        /// </summary>
        public long BeginIndex
        {
            get { return m_TaskConfigData.BeginIndex; }
        }
        /// <summary>
        /// 结束偏移量
        /// </summary>
        public long EndIndex
        {
            get { return m_TaskConfigData.EndIndex; }
        }
        /// <summary>
        /// 当前偏移量（从此处下载）
        /// </summary>
        public long CurIndex
        {
            get { return m_TaskConfigData.CurIndex; }
        }

        /// <summary>
        /// 已下载大小
        /// </summary>
        public long DownloadSize
        {
            get { return CurMIndex - m_TaskConfigData.BeginIndex; }
        }

        #endregion

        #region 事件
        private event EventHandler<TaskInitializedEventArgs> e_TaskInit;
        /// <summary>
        /// 下载任务初始化事件
        /// </summary>
        internal event EventHandler<TaskInitializedEventArgs> TaskInit
        {
            add { lock (m_eventLock) { e_TaskInit += value; } }
            remove { lock (m_eventLock) { e_TaskInit -= value; } }
        }

        private event EventHandler<WriteToStreamEventArgs> e_WriteToMemoryStream;
        /// <summary>
        /// 下载数据写入内存缓存事件
        /// </summary>
        internal event EventHandler<WriteToStreamEventArgs> WriteToMemoryStream
        {
            add { lock (m_eventLock) { e_WriteToMemoryStream += value; } }
            remove { lock (m_eventLock) { e_WriteToMemoryStream -= value; } }
        }

        private event EventHandler<WriteToStreamEventArgs> e_WriteToFileStream;
        /// <summary>
        /// 下载数据写入文件事件
        /// </summary>
        internal event EventHandler<WriteToStreamEventArgs> WriteToFileStream
        {
            add { lock (m_eventLock) { e_WriteToFileStream += value; } }
            remove { lock (m_eventLock) { e_WriteToFileStream -= value; } }
        }

        private event EventHandler<DTaskEventArgs> e_Started;
        /// <summary>
        /// 块下载开始事件
        /// </summary>
        internal event EventHandler<DTaskEventArgs> Started
        {
            add { lock (m_eventLock) { e_Started += value; } }
            remove { lock (m_eventLock) { e_Started -= value; } }
        }

        private event EventHandler<DTaskEventArgs> e_Stopped;
        /// <summary>
        /// 块下载停止事件
        /// </summary>
        internal event EventHandler<DTaskEventArgs> Stopped
        {
            add { lock (m_eventLock) { e_Stopped += value; } }
            remove { lock (m_eventLock) { e_Stopped -= value; } }
        }

        private event EventHandler<DTaskEventArgs> e_Completed;
        /// <summary>
        /// 块下载完成事件
        /// </summary>
        internal event EventHandler<DTaskEventArgs> Completed
        {
            add { lock (m_eventLock) { e_Completed += value; } }
            remove { lock (m_eventLock) { e_Completed -= value; } }
        }

        private event EventHandler<TaskThreadErrorEventArgs> e_Error;
        /// <summary>
        /// 块下载错误事件
        /// </summary>
        internal event EventHandler<TaskThreadErrorEventArgs> Error
        {
            add { lock (m_eventLock) { e_Error += value; } }
            remove { lock (m_eventLock) { e_Error -= value; } }
        }
        private event EventHandler<DTaskEventArgs> e_Failed;
        /// <summary>
        ///  块下载失败事件
        /// </summary>
        internal event EventHandler<DTaskEventArgs> Failed
        {
            add { lock (m_eventLock) { e_Failed += value; } }
            remove { lock (m_eventLock) { e_Failed -= value; } }
        }

        #endregion


        private WorkThreadState m_State;
        /// <summary>
        /// 获取当前线程状态
        /// </summary>
        public WorkThreadState ThreadState
        {
            get { return m_State; }
        }
        /// <summary>
        /// 设置/获取 线程状态 （仅内部使用，触发事件）
        /// </summary>
        protected WorkThreadState State
        {
            get { return m_State; }
            set
            {
                m_State = value;
                // 注意，所以涉及线程状态变更的事件都在此处理
                switch (m_State)
                {
                    case WorkThreadState.Completed:
                        if (e_Completed != null)
                        {
                            // 代理触发 分段下载完成 事件
                            m_TaskMag.EventProxy.AddEvent(delegate()
                            {
                                e_Completed(this, new DTaskEventArgs());
                            });
                        }
                        m_Thread = null;
                        break;
                    case WorkThreadState.Failed:
                        if (e_Failed != null)
                        {
                            // 代理触发 线程失败 事件
                            m_TaskMag.EventProxy.AddEvent(delegate()
                            {
                                e_Failed(this, new DTaskEventArgs());
                            });
                        }
                        m_Thread = null;
                        break;
                    case WorkThreadState.Started:
                        if (e_Started != null)
                        {
                            // 触发 线程开始 事件
                            e_Started(this, new DTaskEventArgs());
                        }
                        break;
                    case WorkThreadState.Stopped:
                        if (e_Stopped != null)
                        {
                            // 触发 线程停止 事件
                            e_Stopped(this, new DTaskEventArgs());
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        /// <summary>
        /// 错误处理
        /// </summary>
        /// <param name="exp"></param>
        private void onError(Exception exp)
        {
            if (this.IsCurrentRunning)
            {
                // 不触发Stopped事件
                m_State = WorkThreadState.Stopped;
                if (e_Error != null)
                {
                    // 代理触发 线程错误 事件
                    m_TaskMag.EventProxy.AddEvent(delegate()
                    {
                        m_ErrorCount++;
                        e_Error(this, new TaskThreadErrorEventArgs(exp));
                    });
                }
                //m_Thread = null;
            }
        }
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="fileInfo"></param>
        private void onTaskInit(RemoteFileInfo fileInfo)
        {
            m_AcceptRanges = fileInfo.AcceptRanges;
            if (this.IsCurrentRunning)
            {
                if (e_TaskInit != null)
                {
                    // 线程初始化 事件  （必须在当前子线程触发）
                    e_TaskInit(this, new TaskInitializedEventArgs(fileInfo));
                }
                else
                {
                    // 默认单线程下载初始化
                    SetConfigData(0, fileInfo.FileSize - 1);
                }
            }
        }

        #region 线程控制

        /// <summary>
        /// 线程锁
        /// </summary>
        private readonly Object m_threadLock = new Object();
        /// <summary>
        /// 启动工作线程
        /// </summary>
        public void Start()
        {
            if (m_State != WorkThreadState.Started && !IsThreadAlive && !IsCompleted)
            {
                lock (m_threadLock)
                {
                    if (m_State != WorkThreadState.Started && !IsThreadAlive && !IsCompleted)
                    {
                        m_Thread = new Thread(this.ThreadWorkInit);
                        m_Thread.Start();
                        State = WorkThreadState.Started;
                    }
                }
            }
        }

        /// <summary>
        /// 重新启动工作线程
        /// </summary>
        public void ReStart()
        {   // 仅在子线程外调用
            Stop();
            Start();
        }

        /// <summary>
        /// 停止当前线程
        /// </summary>
        public void Stop()
        {   // 仅在子线程外调用
            if (m_State == WorkThreadState.Started && IsThreadAlive)
            {
                lock (m_threadLock)
                {
                    if (m_State == WorkThreadState.Started && IsThreadAlive)
                    {
                        m_Thread.Abort();
                        m_Thread = null;
                        State = WorkThreadState.Stopped;
                    }
                    else
                    {
                        m_Thread = null;
                        if (m_State == WorkThreadState.Started)
                        {
                            m_State = WorkThreadState.Stopped;
                        }
                    }
                }
            }
            else
            {
                m_Thread = null;
                if (m_State == WorkThreadState.Started)
                {
                    m_State = WorkThreadState.Stopped;
                }
            }
        }

        /// <summary>
        /// 重置线程块为未初始化状态
        /// </summary>
        internal void Reset()
        {
            e_Completed = null;
            e_Error = null;
            e_Started = null;
            e_Stopped = null;
            e_TaskInit = null;
            e_WriteToFileStream = null;
            e_WriteToMemoryStream = null;
            m_IsInitialized = false;
        }

        internal void Redownload()
        {
        }

        #endregion

        #region 【新】任务线程处理（下载数据）
        /// <summary>
        /// 下载初始化线程
        /// </summary>
        private void ThreadWorkInit()
        {
            try
            {
                // 文件分段尚未初始化，获取下载文件大小，触发下载任务分段初始化事件
                if (!m_IsDataInitialized)
                {
                    IProtocolProvider provider = ProtocolProviderFactory.GetProvider(m_Location.URL);
                    System.IO.Stream ns;
                    onTaskInit(provider.GetFileInfo(m_Location, m_TaskConfigData.Proxy, out ns));
                    if (m_IsDataInitialized && this.IsCurrentRunning)
                    {
                        ThreadWork(ns);  // 开始下载数据
                    }
                }
                else if ((EndIndex > -1 && CurMIndex < EndIndex) || (EndIndex < 0 && !m_isCompleted))
                {
                    IProtocolProvider provider = ProtocolProviderFactory.GetProvider(m_Location.URL);
                    if (m_AcceptRanges)
                    {
                        // 获得服务器回应数据流
                        System.IO.Stream ns = provider.CreateStream(m_Location, m_TaskConfigData.Proxy, CurMIndex, EndIndex);
                        ThreadWork(ns);
                    }
                    else
                    {
                        // 重新开始下载
                        SetConfigData(0, EndIndex);
                        // 获得服务器回应数据流
                        System.IO.Stream ns = provider.CreateStream(m_Location, m_TaskConfigData.Proxy, CurMIndex, EndIndex);
                        ThreadWork(ns);
                    }
                }
            }
            catch (System.Security.SecurityException exp1)
            {   // 没有权限 / 用户名密码错误
                onError(exp1);
                return;
            }
            catch (ThreadAbortException exp)
            {
                // 取消线程，如果需要 处理缓冲区数据
                throw;
            }
            catch (Exception exp1)
            {
                onError(exp1);
                return;
            }
        }

        private void ThreadWork(System.IO.Stream ns)
        {
            try
            {
                byte[] nbytes = new byte[512];
                m_isCompleted = (this.EndIndex > -1 && this.CurMIndex > this.EndIndex);
                while (!m_isCompleted && this.IsCurrentRunning)
                {
                    // 读取数据
                    int nReadSize = (EndIndex > 0) && (512 > (int)(1 + EndIndex - CurMIndex)) ? (int)(1 + EndIndex - CurMIndex) : 512;
                    nReadSize = ns.Read(nbytes, 0, nReadSize);
                    // 判断是否下载完成
                    if (nReadSize > 0)
                    {
                        // 将数据写入内存缓冲区
                        this.WriteToMemory(nbytes, 0, nReadSize);
                        // 如果已知文件大小，则当 当前下载偏移量 大于 结束偏移量 时为下载完成
                        m_isCompleted = (this.EndIndex > -1 && this.CurMIndex > this.EndIndex);
                    }
                    else
                    {
                        // 读取字节数为0时，下载完成
                        m_isCompleted = true;
                    }
                    // 速度限制
                    while (m_TaskMag.Holding)
                    {
                        Thread.Sleep(100);
                    }
                    // 循环读取数据
                }
                if (this.IsDownloaded)
                {
                    if (this.IsCurrentRunning)
                    {   // 完成下载
                        State = WorkThreadState.Completed;
                    }
                    return;
                }
                else
                {   // 未完成，结束线程，等待重试
                    throw (new Exception("非正常的数据结尾！"));
                }
            }
            finally
            {
                ns.Close();
            }
        }


        #endregion

        #region 公有方法

        /// <summary>
        /// 重新设置分段信息
        /// </summary>
        /// <param name="beginIndex">分段起始位置</param>
        /// <param name="endIndex">分段结束位置</param>
        public void SetConfigData(long beginIndex, long endIndex)
        {
            lock (m_mstreamLock)
            {
                m_TaskConfigData.BeginIndex = beginIndex;
                m_TaskConfigData.CurIndex = beginIndex;
                m_TaskConfigData.EndIndex = endIndex;
                if (m_mstream.Length > 0)
                {
                    m_mstream = new MemoryStream();
                }
                m_IsDataInitialized = true;
            }
        }
        /// <summary>
        /// 写入内存缓冲区
        /// [在子线程内触发事件]
        /// </summary>
        /// <param name="buffer">数据源数组</param>
        /// <param name="offset">buffer 中的字节偏移量，从此处开始写入</param>
        /// <param name="count">写入的字节数</param>
        public void WriteToMemory(byte[] buffer, int offset, int count)
        {
            lock (m_mstreamLock)
            {
                m_mstream.Write(buffer, offset, count);
            }
            // 触发 缓存写入 事件
            if (e_WriteToMemoryStream != null)
            {
                e_WriteToMemoryStream(this, new WriteToStreamEventArgs(count));
            }
        }

        /// <summary>
        /// 将内存中的缓冲数据写入文件
        /// [在主线程触发事件]
        /// </summary>
        /// <param name="fs">文件流对象</param>
        public void WriteToFile(FileStream fs)
        {
            // 注意：停止任务时，FileStream已关闭，但可能会与全局保存冲突
            if (m_mstream.Length > 0 && !IsCompleted)
            {
                MemoryStream tmpStream;

                lock (m_mstreamCountLock)
                {
                    lock (m_mstreamLock)
                    {
                        tmpStream = m_mstream;
                        m_mstream = new MemoryStream();
                    }
                    long len = tmpStream.Length;
                    if (len > 0)
                    {
                        // 文件定位
                        fs.Seek(m_TaskConfigData.CurIndex, SeekOrigin.Begin);
                        fs.Write(tmpStream.ToArray(), 0, (int)len);
                        // 更新当前已下载位置
                        m_TaskConfigData.CurIndex += len;
                        tmpStream.Close();

                        // 触发文件写入事件
                        if (e_WriteToFileStream != null)
                        {
                            e_WriteToFileStream(this, new WriteToStreamEventArgs(len));
                        }
                    }
                }
            }
        }

        #endregion


        #region IDisposable 成员
        private bool m_disposed;
        /// <summary>
        /// 释放由 DTaskConfig 的当前实例使用的所有资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!m_disposed)
            {
                if (disposing)
                {
                    // 在此释放托管资源
                    if (m_mstream != null)
                    {
                        m_mstream.Close();
                        m_mstream.Dispose();
                    }
                }

                // 在此释放非托管资源

                m_disposed = true;
            }
        }
        ~DTaskConfig()
        {
            Dispose(false);
        }
        #endregion
    }

    #endregion
}
